<!--
 * @Author: 小花
 * @Date: 2022-05-22 13:04:10
 * @LastEditors: 小花
 * @LastEditTime: 2022-05-28 18:22:50
 * @FilePath: /permission_client/src/views/MenuView.vue
 * @Description: 菜单管理
-->
<template>
  <el-tag class="countBox">
    <span>已选择 <span class="countStyle">{{ multipleSelection.length }}</span> 项</span>
    <el-link type="primary" @click="toggleSelection()" :underline="false">
      <font-awesome-icon icon="eraser" />清空
    </el-link>
    <el-link type="danger" v-if="multipleSelection.length" @click="dropdownDelMenu()" :underline="false">
      <font-awesome-icon icon="trash-can" />批量删除
    </el-link>
  </el-tag>
  <el-table :data="menuList" stripe row-key="id" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
    @selection-change="handleSelectionChange" ref="multipleTableRef"
    :header-cell-style="{ background: '#eef1f6', color: '#606266' }" style="width: 100%">
    <el-table-column type="selection" width="55" />
    <el-table-column label="菜单名称" prop="name" width="160" />
    <el-table-column label="菜单路由" prop="path" width="160">
      <template #default="scope">
        <el-tooltip class="box-item" effect="dark" :content="scope.row.path" placement="top-start">
          {{ scope.row.path }}
        </el-tooltip>
      </template>
    </el-table-column>
    <el-table-column label="组件地址" prop="component" width="160">
      <template #default="scope">
        <el-tooltip class="box-item" effect="dark" :content="`@/${scope.row.component}.vue`" placement="top-start">
          {{ scope.row.component }}
        </el-tooltip>
      </template>
    </el-table-column>
    <el-table-column label="重定向" prop="redirect" width="160">
      <template #default="scope">
        <el-tooltip class="box-item" effect="dark" :content="scope.row.redirect" placement="top-start">
          {{ scope.row.redirect }}
        </el-tooltip>
      </template>
    </el-table-column>
    <el-table-column label="菜单类型" prop="menuType" width="160">
      <template #default="scope">
        {{ menuTypeList.find(item => item.type === scope.row.menuType)?.name }}
      </template>
    </el-table-column>
    <el-table-column label="菜单图标" prop="icon" width="160">
      <template #default="scope">
        <font-awesome-icon v-if="scope.row.icon" :icon="scope.row.icon" />
      </template>
    </el-table-column>
    <el-table-column label="是否路由" prop="isRoute" width="160">
      <template #default="scope">
        <el-tag :type="isOptions.find(item => item.value === scope.row.isRoute)?.type" class="mx-1" effect="dark">
          {{ isOptions.find(item => item.value === scope.row.isRoute)?.label || '空' }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column label="是否子菜单" prop="isLeaf" width="160">
      <template #default="scope">
        <el-tag :type="isOptions.find(item => item.value === scope.row.isLeaf)?.type" class="mx-1" effect="dark">
          {{ isOptions.find(item => item.value === scope.row.isLeaf)?.label || '空' }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column label="是否缓存" prop="keepAlive" width="160">
      <template #default="scope">
        <el-tag :type="isOptions.find(item => item.value === scope.row.keepAlive)?.type" class="mx-1" effect="dark">
          {{ isOptions.find(item => item.value === scope.row.keepAlive)?.label || '空' }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column label="是否隐藏" prop="hidden" width="160">
      <template #default="scope">
        <el-tag :type="isOptions.find(item => item.value === scope.row.hidden)?.type" class="mx-1" effect="dark">
          {{ isOptions.find(item => item.value === scope.row.hidden)?.label || '空' }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column label="创建者" prop="createBy" width="160" />
    <el-table-column label="创建时间" prop="createTime" width="160" />
    <el-table-column label="更新者" prop="updateBy" width="160" />
    <el-table-column label="更新时间" prop="updateTime" width="160" />
    <el-table-column fixed="right" width="180">
      <template #header>
        <el-button @click="dialogFormVisible = true" type="primary">
          <font-awesome-icon icon="plus" />新增菜单
        </el-button>
      </template>
      <template #default="scope">
        <div class="rightFixed">
          <el-link type="primary" :underline="false" @click="execEditMenuPermission(scope.row)">编辑</el-link>
          <el-dropdown>
            <span class="el-dropdown-link">
              更多
              <font-awesome-icon icon="chevron-down" />
            </span>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item>
                  <el-popconfirm title="确定要删除码?" confirm-button-text="确定" cancel-button-text="取消"
                    @confirm="dropdownDelMenu(scope.row)">
                    <template #reference>
                      删除
                    </template>
                  </el-popconfirm>
                </el-dropdown-item>
                <el-dropdown-item @click="dropdownAddLower(scope.row)">
                  添加下级
                </el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </div>
      </template>
    </el-table-column>
  </el-table>

  <el-dialog v-model="dialogFormVisible" title="添加菜单权限" :before-close="beforeClose">
    <el-form :model="form" ref="ruleFormRef" :rules="rules" :label-position="'right'" label-width="6rem">
      <el-form-item label="菜单类型">
        <el-radio-group v-model="form.menuType">
          <el-radio v-for="item in menuTypeList" :key="item.id" :label="item.type"> {{ item.name }} </el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="菜单名称" prop="name">
        <el-input v-model="form.name" placeholder="请输入菜单的名称" />
      </el-form-item>
      <el-form-item label="菜单地址" prop="path">
        <el-tooltip class="box-item" effect="dark" :content="`${axios.defaults.baseURL}${form.path || '...'}`"
          placement="top-start">
          <el-input v-model="form.path" placeholder="请输入菜单地址" />
        </el-tooltip>
      </el-form-item>
      <el-form-item label="父级菜单" v-show="form.menuType == 1" prop="parentId">
        <el-select v-model="form.parentId" class="m-2" placeholder="请选择父级菜单">
          <el-option v-for="item in menuList" :disabled="item.id === form.id" :key="item.id" :label="item.name"
            :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="前端组件" prop="component">
        <el-tooltip class="box-item" effect="dark" :content="`@/${form.component || '...'}.vue`" placement="top-start">
          <el-input v-model="form.component" placeholder="请输入前端组件地址" />
        </el-tooltip>
      </el-form-item>
      <el-form-item label="默认跳转地址">
        <el-input v-model="form.redirect" placeholder="请输入跳转地址" />
      </el-form-item>
      <el-form-item label="菜单图标">
        <el-input class="menuIocn" v-model="form.icon" placeholder="请选择菜单图标">
          <template #prepend>
            <font-awesome-icon v-if="form.icon" :icon="form.icon" />
          </template>
          <template #append>
            <div @click="shwoIconList = true">
              <font-awesome-icon icon="gear" />
            </div>
          </template>
        </el-input>
      </el-form-item>
      <el-form-item label="是否路由菜单">
        <el-switch v-model="form.isRoute" inline-prompt active-text="是" inactive-text="否" />
      </el-form-item>
      <el-form-item label="是否缓存路由">
        <el-switch v-model="form.keepAlive" inline-prompt active-text="是" inactive-text="否" />
      </el-form-item>
      <el-form-item label="是否隐藏路由">
        <el-switch v-model="form.hidden" inline-prompt active-text="是" inactive-text="否" />
      </el-form-item>
    </el-form>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="resetForm(ruleFormRef)">Cancel</el-button>
        <el-button type="primary" @click="submitForm(ruleFormRef)">Confirm</el-button>
      </span>
    </template>
  </el-dialog>
  <icon-list @selectIconValue="selectIconValue" v-if="shwoIconList"></icon-list>
</template>

<script lang='ts'>
import { IMenuType } from '@/model/MenuTypeView'
import MenuView, { EMenuType, IMenu, isOptions, queryMenuAllAndChildren, rules } from '@/model/MenuView'
import { delMenuPermission, pullAllMenuPermission, pullAllMenuType, updateMenuPermission } from '@/request'
import axios from '@/request/axios'
import { responseEnum, responseUtil } from '@/request/responseUtil'
import { DialogBeforeCloseFn, ElMessage, FormInstance } from 'element-plus'
import { defineAsyncComponent, defineComponent, reactive, toRefs } from 'vue'
import { handlerMenu } from '@/model/LoginView'

export default defineComponent({
  name: 'MenuView',
  components: {
    IconList: defineAsyncComponent(() => import('@/components/IconList.vue'))
  },
  setup () {
    const coreData = reactive(new MenuView())

    /**
     * @description: 获取所有菜单类型
     * @param {*}
     * @return {*}
     */
    const execpullAllMenuType = async () => {
      const result = await (await pullAllMenuType()).data as responseUtil<IMenuType[]>
      if (result.code === responseEnum.OK) {
        coreData.menuTypeList = result.data
      }
    }

    /**
     * @description: 获取所有菜单权限
     * @param {*}
     * @return {*}
     */
    const execpullAllMenuPermission = async () => {
      const result = await (await pullAllMenuPermission()).data as responseUtil<IMenu[]>
      if (result.code === responseEnum.OK) {
        coreData.menuList = result.data.filter(it => !it.parentId)
        handlerMenu(result.data.filter(it => it.parentId), coreData.menuList)
      } else if (result.code === responseEnum.FAIL) {
        ElMessage.error(result.msg)
      }
    }

    execpullAllMenuType()
      .then(() => execpullAllMenuPermission())

    /**
     * @description: 提交表单验证
     * @param {*} formEl
     * @return {*}
     */
    const submitForm = async (formEl: FormInstance | undefined) => {
      if (!formEl) return
      await formEl.validate((valid, fields) => {
        if (valid) {
          execupdateMenuPermission()
        } else {
          ElMessage.error(`error submit!, ${JSON.stringify(fields)}`)
          console.log('error submit!', fields)
        }
      })
    }

    /**
     * @description: 重置表单
     * @param {*} formEl
     * @return {*}
     */
    const resetForm = (formEl: FormInstance | undefined) => {
      if (!formEl) return
      formEl.resetFields()
    }

    /**
     * @description: 更新或添加菜单权限
     * @param {*}
     * @return {*}
     */
    const execupdateMenuPermission = async () => {
      // 子菜单升级为一级菜单时，需要清空原上级菜单id
      if (coreData.form.menuType === EMenuType.一级菜单) coreData.form.parentId = ''
      if (coreData.form.id === coreData.form.parentId) {
        ElMessage.error('不能向菜单本身降级')
        return
      }
      const name = coreData.form.component
      coreData.form.routerName = name.substring(name.lastIndexOf('/') + 1)
      localStorage.getItem('')
      const result = await (await updateMenuPermission(coreData.form)).data as responseUtil<IMenu>
      if (result.code === responseEnum.OK) {
        execpullAllMenuPermission()
        coreData.dialogFormVisible = false
        ElMessage.success(result.msg)
      } else {
        ElMessage.error(result.msg)
      }
    }

    /**
     * @description: 更多-删除菜单
     * @param {*} row
     * @return {*}
     */
    const dropdownDelMenu = async (row?: IMenu) => {
      let idList = [] as string[]
      if (row) {
        idList = queryMenuAllAndChildren([row]).map(item => item.id)
      } else {
        idList = queryMenuAllAndChildren(coreData.multipleSelection).map(item => item.id)
      }

      const result = await (await delMenuPermission(idList)).data as responseUtil
      if (result.code === responseEnum.OK) {
        execpullAllMenuPermission()
        ElMessage.success(result.msg)
      } else {
        ElMessage.error(result.msg)
      }
    }

    /**
     * @description: 更多-添加下级
     * @param {*} row
     * @return {*}
     */
    const dropdownAddLower = (row: IMenu) => {
      const data = {
        parentId: row.id,
        path: row.path + '/',
        menuType: EMenuType.子菜单,
        isRoute: true,
        keepAlive: false,
        hidden: false
      } as IMenu
      execEditMenuPermission(data)
    }

    /**
     * @description: 编辑菜单权限
     * @param {*} row
     * @return {*}
     */
    const execEditMenuPermission = async (row: IMenu) => {
      coreData.form = JSON.parse(JSON.stringify(row))
      coreData.dialogFormVisible = true
    }

    /**
     * @description: 回调函数，传递给子组件 IconList 图标选择组件
     * @param {*} icon 图标值
     * @return {*}
     */
    const selectIconValue = (icon: string) => {
      coreData.form.icon = icon || coreData.form.icon
      coreData.shwoIconList = false
    }

    /**
     * @description: 关闭编辑对话框前，数据清空
     * @param {*} done 执行关闭
     * @return {*}
     */
    const beforeClose: DialogBeforeCloseFn = (done) => {
      resetForm(coreData.ruleFormRef)
      done()
    }

    /**
     * @description: 表格多选
     * @param {*} val
     * @return {*}
     */
    const handleSelectionChange = (val: IMenu[]) => {
      coreData.multipleSelection = queryMenuAllAndChildren(val)
    }

    /**
     * @description: 表格多选 选择状态切换
     * @param {*} rows
     * @return {*}
     */
    const toggleSelection = (rows?: IMenu[]) => {
      if (coreData.multipleTableRef) {
        if (rows) {
          rows.forEach((row) => coreData.multipleTableRef.toggleRowSelection(row, false))
        } else {
          coreData.multipleTableRef.clearSelection()
        }
      }
    }
    return {
      ...toRefs(coreData),
      axios,
      isOptions,
      rules,
      selectIconValue,
      execEditMenuPermission,
      dropdownDelMenu,
      dropdownAddLower,
      beforeClose,
      handleSelectionChange,
      toggleSelection,
      submitForm,
      resetForm
    }
  }
})
</script>

<style lang='scss' scoped>
.el-tag.countBox {
  width: 100%;
  height: 2rem;
  text-align: left;
  display: flex;
  justify-content: left;
  align-items: center;
  color: gray;
  font-weight: bold;
  font-size: 14px;
  padding: 1.2rem 2rem;
  margin: 0.6rem 0;

  .el-link {
    margin-left: 1rem;
  }

  .countStyle {
    color: #409EFF;
  }
}

.el-table {

  :deep(.cell) {
    word-break: normal;

    .rightFixed {
      display: flex;
      align-items: center;

      .el-dropdown-link {
        cursor: pointer;
        color: var(--el-color-primary);
      }

      .el-link {
        margin-right: 1rem;
      }
    }
  }
}

.menuIocn {
  :deep(.el-input-group__append) {
    padding: 0;
    width: 3rem;

    div {
      cursor: pointer;
    }
  }
}
</style>
